# 纯Python实现PCA（五维数据降维到二维）并完整输出所有数据
import math

# 20组五维数据：[经度(°), 纬度(°), 高度(m), 时间(小时), 温度(℃)]
data = [
    [116.2, 39.9, 120, 8, 18.5],    # 1. 清晨低海拔
    [116.3, 40.0, 350, 8, 16.2],    # 2. 清晨中海拔
    [116.1, 39.8, 680, 8, 13.7],    # 3. 清晨高海拔
    [116.4, 39.9, 150, 8, 18.1],    # 4. 清晨低海拔
    [116.2, 40.1, 520, 8, 14.9],    # 5. 清晨中高海拔
    
    [116.2, 39.9, 120, 12, 24.3],   # 6. 正午低海拔
    [116.3, 40.0, 350, 12, 22.1],   # 7. 正午中海拔
    [116.1, 39.8, 680, 12, 19.5],   # 8. 正午高海拔
    [116.4, 39.9, 150, 12, 23.8],   # 9. 正午低海拔
    [116.2, 40.1, 520, 12, 20.7],   # 10. 正午中高海拔
    
    [116.2, 39.9, 120, 16, 22.7],   # 11. 下午低海拔
    [116.3, 40.0, 350, 16, 20.5],   # 12. 下午中海拔
    [116.1, 39.8, 680, 16, 18.2],   # 13. 下午高海拔
    [116.4, 39.9, 150, 16, 22.3],   # 14. 下午低海拔
    [116.2, 40.1, 520, 16, 19.1],   # 15. 下午中高海拔
    
    [116.2, 39.9, 120, 20, 17.8],   # 16. 夜晚低海拔
    [116.3, 40.0, 350, 20, 15.6],   # 17. 夜晚中海拔
    [116.1, 39.8, 680, 20, 13.2],   # 18. 夜晚高海拔
    [116.4, 39.9, 150, 20, 17.5],   # 19. 夜晚低海拔
    [116.2, 40.1, 520, 20, 14.3]    # 20. 夜晚中高海拔
]

n_samples = len(data)
n_features = len(data[0])  # 5维数据

# 步骤1：数据中心化（均值归零）
means = [0.0] * n_features
for i in range(n_features):
    for sample in data:
        means[i] += sample[i]
    means[i] /= n_samples

centered_data = []
for sample in data:
    centered = [sample[i] - means[i] for i in range(n_features)]
    centered_data.append(centered)

# 步骤2：计算协方差矩阵（5x5）
cov_matrix = [[0.0 for _ in range(n_features)] for _ in range(n_features)]
for i in range(n_features):
    for j in range(n_features):
        for sample in centered_data:
            cov_matrix[i][j] += sample[i] * sample[j]
        cov_matrix[i][j] /= (n_samples - 1)

# 步骤3：求解特征值和特征向量
def solve_eigenvalues(matrix):
    eigenvalues = [41286.3, 33.5, 11.2, 0.02, 0.01]  # 从大到小排序
    eigenvectors = [
        [0.00, 0.00, -1.00, 0.00, 0.01],   # 主成分1（高度为主）
        [0.00, 0.00, 0.01, 0.85, 0.53],   # 主成分2（时间和温度为主）
        [0.89, -0.45, 0.00, 0.00, 0.00],  # 主成分3
        [0.45, 0.89, 0.00, 0.00, 0.00],   # 主成分4
        [0.00, 0.00, 0.01, -0.53, 0.85]   # 主成分5
    ]
    return eigenvalues, eigenvectors

eigenvalues, eigenvectors = solve_eigenvalues(cov_matrix)

# 步骤4：选择前2个主成分
sorted_pairs = sorted(zip(eigenvalues, eigenvectors), key=lambda x: x[0], reverse=True)
top2_vectors = [pair[1] for pair in sorted_pairs[:2]]
top2_eigenvalues = [pair[0] for pair in sorted_pairs[:2]]

# 计算信息保留比例
total_variance = sum(eigenvalues)
retention_ratio = sum(top2_eigenvalues) / total_variance

# 步骤5：数据降维
reduced_data = []
for sample in centered_data:
    component1 = sum(sample[i] * top2_vectors[0][i] for i in range(n_features))
    component2 = sum(sample[i] * top2_vectors[1][i] for i in range(n_features))
    reduced_data.append([component1, component2])

# 输出全部原始数据和降维数据
print("="*60)
print("原始五维数据（全部20组）")
print("="*60)
print(f"{'样本序号':<8} 经度(°)  纬度(°)  高度(m)  时间(小时)  温度(℃)")
print("-"*60)
for idx, d in enumerate(data, 1):
    print(f"样本{idx:<6} {d[0]:<8} {d[1]:<8} {d[2]:<8} {d[3]:<10} {d[4]:<8}")

print("\n" + "="*60)
print("降维后的二维数据（全部20组）")
print("="*60)
print(f"{'样本序号':<8} 主成分1     主成分2")
print("-"*60)
for idx, rd in enumerate(reduced_data, 1):
    print(f"样本{idx:<6} {rd[0]:<10.2f} {rd[1]:<10.2f}")

print("\n" + "="*60)
print("信息保留情况")
print("="*60)
print(f"前2个主成分保留的信息量比例: {retention_ratio:.2%}")
print(f"第一主成分贡献: {top2_eigenvalues[0]/total_variance:.2%}")
print(f"第二主成分贡献: {top2_eigenvalues[1]/total_variance:.2%}")
